function compose(middleware) {
    if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!');
    for (const fn of middleware) {
        if (typeof fn !== 'function')
            throw new TypeError('Middleware must be composed of functions!');
    }

    /**
     * @param {Object} context
     * @return {Promise}
     * @api public
     */

    return function(context, next) {
        // last called middleware #
        let index = -1;
        return dispatch(0);
        function dispatch(i) {
            if (i <= index) return Promise.reject(new Error('next() called multiple times'));
            index = i;
            let fn = middleware[i];
            if (i === middleware.length) fn = next;
            if (!fn) return Promise.resolve();
            try {
                return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
            } catch (err) {
                return Promise.reject(err);
            }
        }
    };
}

const gener = (i) => (ctx, next) => {
    console.log(ctx.beforeCount++);
    console.log('middware ' + i + ' before');
    next();
    console.log('middware ' + i + ' after');
    console.log(ctx.afterCount++);
};

let middlewares = [gener(1), gener(2), gener(3), gener(4), gener(5)];

const run = compose(middlewares);

run({ beforeCount: 0, afterCount: 0 });
